package bsearch.fx; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Optional; import java.util.ResourceBundle; import javax.swing.SwingUtilities; import org.xml.sax.SAXException; import bsearch.algorithms.SearchMethod; import bsearch.algorithms.SearchMethodLoader; import bsearch.app.BehaviorSearch.RunOptions; import bsearch.app.BehaviorSearch; import bsearch.app.BehaviorSearchException; import bsearch.app.SearchProtocol; import bsearch.nlogolink.NetLogoLinkException; import bsearch.representations.ChromosomeFactory; import bsearch.representations.ChromosomeTypeLoader; import bsearch.space.ParameterSpec; import bsearch.space.SearchSpace; import bsearch.util.GeneralUtils; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.control.Alert; import javafx.scene.control.Alert.AlertType; import javafx.scene.control.Button; import javafx.scene.control.ButtonBar.ButtonData; import javafx.scene.control.ButtonType; import javafx.scene.control.CheckBox; import javafx.scene.control.ChoiceBox; import javafx.scene.control.Label; import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.scene.control.TableColumn.CellEditEvent; import javafx.scene.control.TableView; import javafx.scene.control.TextArea; import javafx.scene.control.TextField; import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.image.Image; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.GridPane; import javafx.scene.layout.Priority; import javafx.scene.web.WebView; import javafx.stage.FileChooser; import javafx.stage.Modality; import javafx.stage.FileChooser.ExtensionFilter; import javafx.stage.Stage; import javafx.stage.Window; import javafx.application.Platform; import javafx.beans.property.SimpleStringProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.event.ActionEvent; import javafx.event.EventHandler; public class MainController implements Initializable { // component outside of tab will have normal name @FXML public AnchorPane anchorPane; @FXML public TextField browseField; @FXML public Button runButton; @FXML public Button browseButton; // component in Model tab will start with M @FXML public TextArea MParamSpecsArea; @FXML public Button MHelpSearchSpaceButton; @FXML public Button MSuggestParamButton; @FXML public TextField MModelStepField; @FXML public TextField MModelSetupField; @FXML public TextField MModelStopConditionField; @FXML public TextField MModelStepLimitField; @FXML public TextField MMeasureField; @FXML public TextField MMeasureIfField; // component in Search Objective tab will start with SO @FXML public ChoiceBox<String> SOGoalBox; @FXML public ChoiceBox<String> SOFitnessCollectingBox; @FXML public TextField SOFitnessSamplingRepetitionsField; @FXML public ChoiceBox<String> SOFixedSamplingBox; @FXML public ChoiceBox<String> SOCombineReplicatesBox; @FXML public ChoiceBox<String> SOWrtBox; @FXML public CheckBox SOTakeDerivativeCheckBox; @FXML public TextField SODeltaField; @FXML public Label SOWrtLabel; @FXML public Label SODeltaLabel; @FXML public CheckBox SOFitnessDerivativeUseAbsCheckBox; @FXML public Button SOHelpEvaluationButton; // component in Search Algorithm tab will start with SA @FXML public ChoiceBox<String> SASearchMethodBox; @FXML public ChoiceBox<String> SAChromosomeTypeBox; @FXML public CheckBox SACachingCheckBox; @FXML public TextField SABestCheckingField; @FXML public TextField SAEvaluationLimitField; @FXML public TableView<SearchMethodParamTableRow> SASearchMethodTable; @FXML public TableColumn<SearchMethodParamTableRow, String> SAParamCol; @FXML public TableColumn<SearchMethodParamTableRow, String> SAValCol; @FXML public Button SAHelpSearchSpaceRepresentationButton; @FXML public Button SAHelpSearchMethodButton; // other component that not in GUI private File defaultUserDocumentsFolder = new FileChooser().getInitialDirectory(); private String defaultProtocolXMLForNewSearch; private HashMap<String, SearchMethod> searchMethodChoices = new HashMap<String, SearchMethod>(); private File currentFile; private String lastSavedText; protected RunOptions runOptions; Image icon = new Image(GeneralUtils.getResource("icon_behaviorsearch.png").toURI().toString()); @Override public void initialize(URL arg0, ResourceBundle arg1) { // set up ChoiceBox in SO tab SOGoalBox.setItems(FXCollections.observableArrayList("Minimize Fitness", "Maximize Fitness")); List<String> fitnessCollecting = new ArrayList<String>(); for (SearchProtocol.FITNESS_COLLECTING f : SearchProtocol.FITNESS_COLLECTING.values()) { fitnessCollecting.add(f.toString()); } SOFitnessCollectingBox.setItems(FXCollections.observableArrayList(fitnessCollecting)); SOFixedSamplingBox.setItems(FXCollections.observableArrayList("Fixed Sampling")); List<String> combineReplication = new ArrayList<String>(); for (SearchProtocol.FITNESS_COMBINE_REPLICATIONS f : SearchProtocol.FITNESS_COMBINE_REPLICATIONS.values()) { combineReplication.add(f.toString()); } SOCombineReplicatesBox.setItems(FXCollections.observableArrayList(combineReplication)); SOWrtBox.setItems(FXCollections.observableArrayList("---")); // set up ChoiceBox in SA tab try { SASearchMethodBox.setItems(FXCollections.observableArrayList(SearchMethodLoader.getAllSearchMethodNames())); } catch (BehaviorSearchException e) { handleError(e.getMessage()); } try { for (String name : SearchMethodLoader.getAllSearchMethodNames()) { searchMethodChoices.put(name, SearchMethodLoader.createFromName(name)); } } catch (BehaviorSearchException e1) { handleError(e1.getMessage()); } SASearchMethodBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>() { @Override public void changed(ObservableValue<? extends String> obsValue, String oldSelectedStage, String newSelectedStage) { SearchMethod searchMethod = searchMethodChoices.get(newSelectedStage); updateSearchMethodParamTable(searchMethod, searchMethod.getSearchParams()); } }); try { SAChromosomeTypeBox .setItems(FXCollections.observableArrayList(ChromosomeTypeLoader.getAllChromosomeTypes())); } catch (BehaviorSearchException e) { handleError(e.getMessage()); } // set up field that not in GUI try { defaultProtocolXMLForNewSearch = GeneralUtils .stringContentsOfFile(GeneralUtils.getResource("defaultNewSearch.xml")); } catch (java.io.FileNotFoundException ex) { handleError("Cannot find defaultNewSearch.xml",null); System.exit(1); } actionNew(); } public void helpDialog(String title, String content){ Alert alert = new Alert(AlertType.INFORMATION); alert.setTitle(title); alert.setHeaderText(null); WebView webView = new WebView(); webView.getEngine().loadContent(content); webView.setPrefSize(500, 300); alert.getDialogPane().setContent(webView);; alert.showAndWait(); } public void helpSearchSpaceAction(ActionEvent event) { helpDialog("Help about search space specification", "<HTML><BODY>" + "Specifying the range of parameters to be searched works much the same as the BehaviorSpace tool in NetLogo:" + "<PRE> [ \"PARAM_NAME\" VALUE1 VALUE2 VALUE3 ... ] </PRE>" + "or <PRE> [ \"PARAM_NAME\" [RANGE_START INCREMENT RANGE_END] ] </PRE>" + "<P>One slight difference is that INCREMENT may be \"C\", which means to search the range continously " + "(or at least with fine resolution, if the chromosomal representation doesn't allow for continuous parameters)</P>" + "</BODY></HTML>"); } public void helpSearchSpaceRepresentationAction(ActionEvent event) { String chromosomeType = SAChromosomeTypeBox.getValue(); try { ChromosomeFactory factory = ChromosomeTypeLoader.createFromName(chromosomeType); helpDialog("Help about " + chromosomeType, factory.getHTMLHelpText() + "<BR><BR>"); } catch (BehaviorSearchException ex) { handleError(ex.toString()); } } public void helpEvaluationAction(ActionEvent event) { helpDialog("Help about fitness evaluation", "<HTML><BODY>" + "An objective function must condense the data collected from multiple model runs into a single number, " + "which is what the search process will either attempt to minimize or maximize." + "</BODY></HTML>"); } public void helpSearchMethodAction(ActionEvent event) { SearchMethod sm = searchMethodChoices.get(SASearchMethodBox.getValue()); helpDialog("Help about " + sm.getName(), sm.getHTMLHelpText()); } private Window getMainWindow() { if (anchorPane != null && anchorPane.getScene() != null) { return anchorPane.getScene().getWindow(); } else { return null; } } public void showTutorialAction(ActionEvent event) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { org.nlogo.swing.BrowserLauncher.openURL(null,GeneralUtils.attemptResolvePathFromBSearchRoot("documentation/tutorialFx.html"),true); } }); } public void showAboutAction(ActionEvent event) { Alert alert = new Alert(AlertType.CONFIRMATION); alert.setTitle("About BehaviorSearch..."); alert.setHeaderText(null); alert.setGraphic(null); ButtonType browseWebsite = new ButtonType("Browse BehaviorSearch web site"); ButtonType close = new ButtonType("Close", ButtonData.CANCEL_CLOSE); alert.getButtonTypes().setAll(browseWebsite, close); TextArea content = new TextArea(); File creditsFile = new File(GeneralUtils.attemptResolvePathFromBSearchRoot("CREDITS.TXT")); File licFile = new File(GeneralUtils.attemptResolvePathFromBSearchRoot("LICENSE.TXT")); String creditsText, licText; try { creditsText = GeneralUtils.stringContentsOfFile(creditsFile); licText = GeneralUtils.stringContentsOfFile(licFile); } catch (FileNotFoundException ex) { creditsText = "ERROR: Either CREDITS.TXT or LICENSE.TXT file not found."; licText = ""; } content.setText("BehaviorSearch v" + GeneralUtils.getVersionString() + "\n" + creditsText + "\n*****\n\n" + licText); content.setWrapText(true); content.setMinHeight(400); alert.getDialogPane().setContent(content);; Optional<ButtonType> result = alert.showAndWait(); if (result.get() == browseWebsite){ SwingUtilities.invokeLater(new Runnable() { @Override public void run() { org.nlogo.swing.BrowserLauncher.openURL(null, "http://www.behaviorsearch.org/", false); } }); } // ...otherwise user chose CANCEL or closed the dialog } public void browseFile(ActionEvent event) { FileChooser fileChooser = new FileChooser(); File parentFolder = new File(browseField.getText()).getParentFile(); if (parentFolder != null && parentFolder.exists()) { fileChooser.setInitialDirectory(parentFolder); } File selectedFile = fileChooser.showOpenDialog(getMainWindow()); if (selectedFile != null) { browseField.setText(selectedFile.getPath()); } } private void updateWindowTitle(String fileName) { Window mainWindow = getMainWindow(); if (mainWindow != null) { ((Stage) mainWindow).setTitle(fileName + MainGUI.getWindowTitleSuffix()); } } public void actionNew() { if (!checkDiscardOkay()) { return; } currentFile = null; MParamSpecsArea.setText("[\"integerParameter\" [0 1 10]] \n" + "[\"continuousParameter\" [0.0 \"C\" 1.0]] \n " + "[\"choiceParameter\" \"near\" \"far\"] \n"); SearchProtocol protocol; try { protocol = SearchProtocol.load(defaultProtocolXMLForNewSearch); loadProtocol(protocol); } catch (IOException e) { e.printStackTrace(); throw new IllegalStateException("Error loading default XML protocol to initialize UI!"); } catch (SAXException e) { e.printStackTrace(); throw new IllegalStateException("Error loading default XML protocol to initialize UI!"); } updateWindowTitle("Untitled"); } public void actionOpen() { if (!checkDiscardOkay()) { return; } FileChooser chooser = new FileChooser(); if (currentFile != null) { chooser.setInitialDirectory(currentFile.getParentFile()); ; } chooser.getExtensionFilters().addAll(new ExtensionFilter("bsearch File", "*.bsearch"), new ExtensionFilter("XML File", "*.xml")); File selectedFile = chooser.showOpenDialog(null); if (selectedFile != null) { openFile(selectedFile); } } public void actionOpenExample() { if (!checkDiscardOkay()) { return; } FileChooser chooser = new FileChooser(); try { chooser.setInitialDirectory(new File(GeneralUtils.attemptResolvePathFromBSearchRoot("examples"))); } catch (Exception e) { handleError("Error: cannot find Example folder", e); } chooser.getExtensionFilters().addAll(new ExtensionFilter("bsearch File", "*.bsearch"), new ExtensionFilter("XML File", "*.xml")); File selectedFile = chooser.showOpenDialog(null); if (selectedFile != null) { openFile(selectedFile); } } public void actionOpenTest() { File selectedFile = new File("C:/Users/AnNguyen/Google Drive/behaviorsearch/behaviorsearch/examples/TestForFX.bsearch" ); openFile(selectedFile); } private void openFile(File fProtocol) { try { SearchProtocol protocol = SearchProtocol.loadFile(fProtocol.getPath()); currentFile = fProtocol; loadProtocol(protocol); updateWindowTitle(currentFile.getName()); } catch (IOException e) { handleError("IO Error occurred attempting to load file: " + fProtocol.getPath(),null); e.printStackTrace(); } catch (SAXException e) { handleError("XML Parsing error occurred attempting to load file: " + fProtocol.getPath(),null); e.printStackTrace(); } } public void loadProtocol(SearchProtocol protocol) { browseField.setText(protocol.modelFile); StringBuilder sb = new StringBuilder(); for (String s : protocol.paramSpecStrings) { sb.append(s); sb.append("\n"); } this.MParamSpecsArea.setText(sb.toString()); MModelStepField.setText(protocol.modelStepCommands); MModelSetupField.setText(protocol.modelSetupCommands); MModelStopConditionField.setText(protocol.modelStopCondition); MModelStepLimitField.setText(Integer.toString(protocol.modelStepLimit)); MMeasureField.setText(protocol.modelMetricReporter); MMeasureIfField.setText(protocol.modelMeasureIf); SOGoalBox.setValue(protocol.fitnessMinimized ? "Minimize Fitness" : "Maximize Fitness"); SOFitnessCollectingBox.setValue(protocol.fitnessCollecting.toString()); SOFitnessSamplingRepetitionsField.setText(Integer.toString(protocol.fitnessSamplingReplications)); SOFixedSamplingBox .setValue((protocol.fitnessSamplingReplications != 0) ? "Fixed Sampling" : "Adaptive Sampling"); SOCombineReplicatesBox.setValue(protocol.fitnessCombineReplications.toString()); SOTakeDerivativeCheckBox.setSelected(protocol.fitnessDerivativeParameter.length() > 0); SOFitnessDerivativeUseAbsCheckBox.setSelected(protocol.fitnessDerivativeUseAbs); takeDerivativeAction(new ActionEvent()); SOWrtBox.setValue(protocol.fitnessDerivativeParameter); SODeltaField.setText(Double.toString(protocol.fitnessDerivativeDelta)); SASearchMethodBox.setValue(protocol.searchMethodType); SAChromosomeTypeBox.setValue(protocol.chromosomeType); updateSearchMethodParamTable(searchMethodChoices.get(protocol.searchMethodType), protocol.searchMethodParams); SACachingCheckBox.setSelected(protocol.caching); SABestCheckingField.setText(Integer.toString(protocol.bestCheckingNumReplications)); SAEvaluationLimitField.setText(Integer.toString(protocol.evaluationLimit)); lastSavedText = protocol.toXMLString(); runOptions = null; //the runOptions to defaults, when a different Protocol is loaded } private SearchProtocol createProtocolFromFormData() throws UIConstraintException { HashMap<String, String> searchMethodParams = new java.util.LinkedHashMap<String, String>(); List<SearchMethodParamTableRow> currentTable = SASearchMethodTable.getItems(); for (SearchMethodParamTableRow row : currentTable) { searchMethodParams.put(row.getParam().trim(), row.getValue().trim()); } int modelStepLimit = 0; try { modelStepLimit = Integer.valueOf(MModelStepLimitField.getText()); if (modelStepLimit < 0) { throw new NumberFormatException(); } } catch (NumberFormatException ex) { throw new UIConstraintException("STEP LIMIT should be a non-negative integer.", "Error: can't create search protocol"); } int fitnessSamplingRepetitions = 0; if (SOFixedSamplingBox.getValue().toString().equals("Fixed Sampling")) { try { fitnessSamplingRepetitions = Integer.valueOf(SOFitnessSamplingRepetitionsField.getText()); if (fitnessSamplingRepetitions < 0) { throw new NumberFormatException(); } } catch (NumberFormatException ex) { throw new UIConstraintException( "SAMPLING REPETITIONS should be a positive integer, or 0 if using adaptive sampling.", "Error: can't create protocol"); } } boolean caching = SACachingCheckBox.isSelected(); int evaluationLimit = 0; try { evaluationLimit = Integer.valueOf(SAEvaluationLimitField.getText()); if (evaluationLimit <= 0) { throw new NumberFormatException(); } } catch (NumberFormatException ex) { throw new UIConstraintException("EVALUATION LIMIT should be a positive integer.", "Error: can't create search protocol"); } int bestCheckingNumReplications = 0; try { bestCheckingNumReplications = Integer.valueOf(SABestCheckingField.getText()); if (bestCheckingNumReplications < 0) { throw new NumberFormatException(); } } catch (NumberFormatException ex) { throw new UIConstraintException( "The number of 'BEST CHECKING' replicates should be a non-negative integer.", "Error: can't create search protocol"); } double fitnessDerivDelta = 0.0; if (SOTakeDerivativeCheckBox.isSelected()) { try { fitnessDerivDelta = Double.valueOf(SODeltaField.getText()); } catch (NumberFormatException ex) { throw new UIConstraintException( "The DELTA value (for taking the derivative of the objective fucntion with respect to a parameter) needs to be a number", "Error: can't create search protocol"); } } SearchProtocol protocol = new SearchProtocol(browseField.getText(), java.util.Arrays.asList(MParamSpecsArea.getText().split("\n")), MModelStepField.getText(), MModelSetupField.getText(), MModelStopConditionField.getText(), modelStepLimit, MMeasureField.getText(), MMeasureIfField.getText(), SOGoalBox.getValue().toString().equals("Minimize Fitness"), fitnessSamplingRepetitions, SearchProtocol.FITNESS_COLLECTING.valueOf(SOFitnessCollectingBox.getValue().toString()), SearchProtocol.FITNESS_COMBINE_REPLICATIONS.valueOf(SOCombineReplicatesBox.getValue().toString()), SOTakeDerivativeCheckBox.isSelected() ? SOWrtBox.getValue().toString() : "", fitnessDerivDelta, SOFitnessDerivativeUseAbsCheckBox.isSelected(), SASearchMethodBox.getValue().toString(), searchMethodParams, SAChromosomeTypeBox.getValue().toString(), caching, evaluationLimit, bestCheckingNumReplications); return protocol; } public void actionSave() { if (currentFile == null) { actionSaveAs(); } else { doSave(); } } public void actionSaveAs() { FileChooser chooser = new FileChooser(); chooser.getExtensionFilters().addAll(new ExtensionFilter("bsearch File", "*.bsearch")); File parentFolder = null; if (currentFile != null) { parentFolder = currentFile.getParentFile(); chooser.setInitialFileName(currentFile.getName()); } else { parentFolder = new File(browseField.getText()).getParentFile(); chooser.setInitialFileName("Untitled.bsearch"); } if (parentFolder != null && parentFolder.exists()) { chooser.setInitialDirectory(parentFolder); } File tempFile = chooser.showSaveDialog(null); if (tempFile != null) { currentFile = tempFile; doSave(); } if (currentFile!=null){ updateWindowTitle(currentFile.getName()); } } private void doSave() { java.io.FileWriter fout; try { fout = new java.io.FileWriter(currentFile); SearchProtocol protocol = createProtocolFromFormData(); protocol.save(fout); fout.close(); lastSavedText = protocol.toXMLString(); } catch (IOException ex) { handleError("IO Error occurred attempting to save file: " + currentFile.getPath(),ex); } catch (UIConstraintException ex) { System.out.println(ex.getMessage()); } } public boolean protocolChangedSinceLastSave() { String xmlStr = ""; // Note: lastSavedText == null ONLY when the GUI is being loaded for the // first time. if(lastSavedText==null){ return false; } try { xmlStr = createProtocolFromFormData().toXMLString(); } catch (UIConstraintException ex) { // if we can't create a valid protocol object from the form data, // assume the user has changed something... return true; } return !lastSavedText.equals(xmlStr); } boolean checkDiscardOkay() { boolean check = true; if (protocolChangedSinceLastSave()) { Alert alert = new Alert(AlertType.CONFIRMATION); alert.setTitle("Discard changes?"); alert.setHeaderText("Discard changes you've made to this search experiment?"); Optional<ButtonType> result = alert.showAndWait(); if (result.get() == ButtonType.OK){ check = true; } else { check = false; } } return check; } // public void takeDerivativeAction(ActionEvent event) { if (SOTakeDerivativeCheckBox.isSelected()) { this.SOWrtLabel.setDisable(false); this.SODeltaLabel.setDisable(false); this.SOWrtBox.setDisable(false); this.SODeltaField.setDisable(false); List<String> wrt = new ArrayList<String>(); SearchSpace ss = new SearchSpace(java.util.Arrays.asList(this.MParamSpecsArea.getText().split("\n"))); for (ParameterSpec spec : ss.getParamSpecs()) { wrt.add(spec.getParameterName()); } wrt.add("@MUTATE@"); this.SOWrtBox.setItems(FXCollections.observableArrayList(wrt)); } else { this.SOWrtLabel.setDisable(true); this.SODeltaLabel.setDisable(true); this.SOWrtBox.setDisable(true); this.SODeltaField.setDisable(true); } } public void suggestParam(ActionEvent event) { try { this.MParamSpecsArea.setText(bsearch.nlogolink.Utils.getDefaultConstraintsText(this.browseField.getText())); } catch (NetLogoLinkException e) { handleError(e.getMessage(), e); } } public void actionRunNow(ActionEvent event) { if (runOptions == null) { runOptions = new BehaviorSearch.RunOptions(); //suggest a filename stem for output files, which users can change. if (currentFile != null) { String fnameStem = currentFile.getPath(); fnameStem = fnameStem.substring(0, fnameStem.lastIndexOf('.')); fnameStem = GeneralUtils.attemptResolvePathFromStartupFolder(fnameStem); runOptions.outputStem = fnameStem; } else { //TODO: Use folder where the NetLogo model is located instead? runOptions.outputStem = new File(defaultUserDocumentsFolder, "mySearchOutput").getPath(); } } if (currentFile != null) { runOptions.protocolFilename = this.currentFile.getAbsolutePath(); } Stage stage = new Stage(); Parent root; try { FXMLLoader loader = new FXMLLoader(getClass().getResource("RunOptionDialog.fxml")); root = loader.load(); stage.setScene(new Scene(root)); stage.setTitle("Run Options Dialog"); stage.initModality(Modality.APPLICATION_MODAL); stage.initOwner(runButton.getScene().getWindow()); stage.getIcons().add(icon); RunOptionDialogController runController = loader.getController(); runController.ini(runOptions, this); stage.showAndWait(); } catch (IOException e) { handleError("Required file not found: RunOptionDialog.fxml \nThe program will not run properly"); } } public static void handleError(String msg1, Throwable e) { Platform.runLater(new Runnable() { public void run() { Alert alert = new Alert(AlertType.ERROR); alert.setTitle("Exception Dialog"); alert.setContentText(msg1); if (e!=null) { // Create expandable Exception. StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); e.printStackTrace(pw); String exceptionText = sw.toString(); Label label = new Label("The exception stacktrace was:"); TextArea textArea = new TextArea(exceptionText); textArea.setEditable(false); textArea.setWrapText(true); textArea.setMaxWidth(Double.MAX_VALUE); textArea.setMaxHeight(Double.MAX_VALUE); GridPane.setVgrow(textArea, Priority.ALWAYS); GridPane.setHgrow(textArea, Priority.ALWAYS); GridPane expContent = new GridPane(); expContent.setMaxWidth(Double.MAX_VALUE); expContent.add(label, 0, 0); expContent.add(textArea, 0, 1); // Set expandable Exception into the dialog pane. alert.getDialogPane().setExpandableContent(expContent); } alert.showAndWait(); } }); } public static void handleError(String msg1) { handleError(msg1, null); } public void displayProgressDialog(){ SearchProtocol protocol; try { protocol = createProtocolFromFormData(); } catch (UIConstraintException e) { handleError("Error creating SearchProtocol: " + e.getMessage(),null); return; } Stage stage = new Stage(); Parent root; try { FXMLLoader loader = new FXMLLoader(getClass().getResource("ProgressDialog.fxml")); root = loader.load(); stage.setScene(new Scene(root)); stage.setTitle("Progress Dialog"); stage.initModality(Modality.APPLICATION_MODAL); stage.initOwner(getMainWindow()); stage.getIcons().add(icon); ProgressController progressController = loader.getController(); progressController.startSearchTask(protocol, runOptions); stage.show(); } catch (IOException e) { handleError("Required file not found: ProgressDialog.fxml \nThe program will not run properly"); } } private void updateSearchMethodParamTable(SearchMethod searchMethod, HashMap<String, String> searchMethodParams) { // if the search method in the protocol is missing some parameters, fill // them in with defaults HashMap<String, String> defaultParams = searchMethod.getSearchParams(); for (String key : defaultParams.keySet()) { if (!searchMethodParams.containsKey(key)) { searchMethodParams.put(key, defaultParams.get(key)); } } this.SASearchMethodTable.setEditable(true); List<SearchMethodParamTableRow> paramTable = new ArrayList<SearchMethodParamTableRow>(); for (String s : searchMethodParams.keySet()) { paramTable.add(new SearchMethodParamTableRow(s, searchMethodParams.get(s))); } SAValCol.setCellFactory(AcceptOnExitTableCell.forTableColumn()); // set up table data SAParamCol.setCellValueFactory(new PropertyValueFactory<SearchMethodParamTableRow, String>("param")); SAValCol.setCellValueFactory(new PropertyValueFactory<SearchMethodParamTableRow, String>("value")); this.SASearchMethodTable.setItems(FXCollections.observableArrayList(paramTable)); SAValCol.setOnEditCommit(new EventHandler<CellEditEvent<SearchMethodParamTableRow, String>>() { @Override public void handle(CellEditEvent<SearchMethodParamTableRow, String> t) { t.getTableView().getItems().get(t.getTablePosition().getRow()).setValue(t.getNewValue()); //this code is to test if value actually come back to data //for (SearchMethodParamTableRow i: paramTable){ //System.out.println(i); } } }); } private class UIConstraintException extends Exception { private String title; public UIConstraintException(String msg, String title) { super(msg); this.title = title; } public String getTitle() { return title; } private static final long serialVersionUID = 1L; } }